/* s2 - compare values, considering some Spectre issues
   Copyright (C) 2018, 2019 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef _sHT_TEST_H
#define _sHT_TEST_H

#include <stddef.h>
#include <stdint.h>
#include <sHT/compiler.h>
#include <sHT/test-arch.h>

/** Comparing values

  If two values are compared, the compiler can assume information in both
  branches, which may be -- speculatively -- incorrect. s2 must not allow
  side-channel attacks.

  Why use inline assembly instead of littering the code with
  @var{sHT_hide_var}? Because its a lie that the variable changes,
  reducing optimisation opportunities, because its fragile: information
  about a parameter implies information about other data, because it is
  less verbose, and because branches can easily be marked.

  The last allows for checking the binary that all branches are hidden
  correctly, and allows for flipping and deleting branches to test the test
  cases.

  A major downside is that this requires architecture-specific code. */

/** @var{a} > @var{b}?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_gt(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p(a > b))
		return a > b;
	_sHT_gt(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} >= @var{b}?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_ge(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p(a >= b))
		return a >= b;
	_sHT_ge(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} == @var{b}?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_eq(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p(a == b))
		return a == b;
	_sHT_eq(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} == @var{b} ? 1 : 0

  This differs from @var{sHT_eq} in that the
  return value is an integer, and not a condition.
  It may not be directly branched upon. */
__attribute__((always_inline))
static inline int
sHT_eq_bool(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p(a == b))
		return a == b;
	_Bool ret;
	_sHT_eq_bool(a, b, ret);
	return ret;
}

/** @var{a} != @var{b}?
  The fall-through case should be the most likely. */
static inline _Bool
sHT_neq(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p(a != b))
		return a != b;
	_sHT_neq(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} < 0?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_lt0(intmax_t a)
{
	if (sHT_constant_p(a < 0))
		return a < 0;
	_sHT_lt0(a, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} == 0?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_zero_p(uintmax_t a)
{
	if (sHT_constant_p(a == 0))
		return a == 0;
	_sHT_zero_p(a, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} != 0?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_nonzero_p(uintmax_t a)
{
	if (sHT_constant_p(a != 0))
		return a != 0;
	_sHT_nonzero_p(a, correct);
	return 0;
correct:
	return 1;
}


/** @var{a} == @var{b}? Both are pointers.
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_eq_pointer(void *a, void *b)
{
	if (sHT_constant_p(a == b))
		return a == b;
	_sHT_eq(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} == NULL?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_null_p(void *a)
{
	if (sHT_constant_p(a == NULL))
		return 1;
	_sHT_zero_p(a, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} & @var{b} != 0?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_and_any(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p((a & b) != 0))
		return (a & b) != 0;
	_sHT_and_any_p(a, b, correct);
	return 0;
correct:
	return 1;
}

/** @var{a} & @var{b} == 0?
  The fall-through case should be the most likely. */
__attribute__((always_inline))
static inline _Bool
sHT_and_none(uintmax_t a, uintmax_t b)
{
	if (sHT_constant_p((a & b) == 0))
		return (a & b) == 0;
	_sHT_and_none_p(a, b, correct);
	return 0;
correct:
	return 1;
}

#endif
